#include "title.h"

#include <stdlib.h>
#include <config.h>
#include <assert.h>

#include "global.h"
#include "algorithm.h"
#include "dialog.h"
#include "images.h"
#include "menu.h"
#include "region.h"
#include "palette.h"
#include "text.h"
#include "water.h"

// height of the horizon, in %
static const float HORIZON = 0.8;

// stars
static const float STARS_H = 0.4; // max height
static const int STARS = 30;      // number of stars
static const int STARS_C = 200;   // star initial indexed color
static const int STARS_NC = 6;    // number of star colors

// colors for the palette of this image
static const SDL_Color sky_initial = {  75,  83,  86 },
                       sky_final   = { 255, 166,  86 };
static const int sky_n_colors = 120;

// initialize palette
// colors 0 and 1 are black and white
static SDL_Color color[256];

// main menu manager
static Menu* main_menu;
static int menu_x, menu_y;

// main menu items
static void create_new_region();
static Region* chosen_region = NULL;

/*
static OptionMenu option_menu[] = {
	{ gettext_noop("Fullscreen"), BOOLEAN, &full_screen },
	{ gettext_noop("Back"), BACK },
	{ NULL, 0, NULL }
}; */

static void quit();
static OptionMenu menus[] = {
	{ gettext_noop("Play tutorial"), NOT_IMPLEMENTED }, //SUBMENU, NULL, (void*)TUTORIAL },
	{ gettext_noop("Play scenario"), NOT_IMPLEMENTED }, //SUBMENU, NULL, (void*)SCENARIO },
	{ gettext_noop("Play open-ended game"), SUBMENU, NULL, (void*)OPEN_ENDED },
//	{ gettext_noop("Options"), SUBMENU, option_menu },
	{ "_EXIT_TO_OS", FUNCTION_CALL, &quit },
	{ NULL, 0, NULL }
};


/* The building images work this way: each array is the drawing of the left
 * side of a building. The image is mirrored. For example, the array
 * { 1, 2, 4, 0} would result in the following image:
 *
 *   xx
 *   xx
 *  xxxx
 * xxxxxx */
static int buildings[][10] = {
	{ 24, 40, 41, 44, 44, 60, 0 },
	{ 16, 0 },
	{ 62, 62, 62, 62, 62, 0 },
	{ 42, 42, 42, 42, 0 },
	{ 52, 58, 58, 58, 58, 58, 58, 0 },
	{ 10, 0 },
	{ 45, 45, 45, 47, 47, 48, 48, 49, 49, 0 },
	{ 54, 54, 44, 44, 44, 44, 0 },
	{ 6, 0 },
	{ 30, 30, 48, 49, 50, 51, 51, 0 },
	{ 42, 42, 42, 42, 0 },
	{ 20, 20, 0 },
	{ 24, 25, 26, 34, 0 },
	{ 0 }
};


// Store the object that controls the water cycle
static Water* water;


// Create the palette for the initial image.
static void setup_title_palette()
{
	// setup basic colors
	setup_palette(screen);
	load_default_palette(color);

	// sky palette
	create_gradient(color, sky_initial, sky_final, 15, sky_n_colors + 15);

	// stars
	color[STARS_C+0] = (SDL_Color){ 255, 255, 255 };
	color[STARS_C+1] = (SDL_Color){ 225, 225, 225 };
	color[STARS_C+2] = (SDL_Color){ 175, 175, 175 };
	color[STARS_C+3] = (SDL_Color){ 135, 135, 135 };
	color[STARS_C+4] = (SDL_Color){ 175, 175, 175 };
	color[STARS_C+5] = (SDL_Color){ 225, 225, 225 };

	// water palette
	if(water)
		free_water(water);
	water = water_init(screen, color, (SDL_Color){  70,  91, 130 },
			                  (SDL_Color){ 225, 183, 176 }, 160);

	SDL_SetPalette(screen, SDL_LOGPAL|SDL_PHYSPAL, &color[15], 15, 240);
}


// Draw the sky gradient, with the stars.
static void draw_sky()
{
	int horizon = (float)screen->h * HORIZON;
	double inc = (double)sky_n_colors / (double)horizon;
	double color = 15.0;
	Uint8* pixels = ((Uint8*)screen->pixels);

	// draw sky
	SDL_Rect r = { 0, 0, screen->w, 1 };
	for(r.y=0; r.y<horizon; r.y++)
	{
		SDL_FillRect(screen, &r, color);
		color += inc;
	}

	// draw stars
	srand(0);
	int i = 0;
	int stars_h = (float)screen->h * STARS_H;
	for(i=0; i<STARS; i++)
	{
		int x = rand() % screen->w;
		int y = rand() % stars_h;
		pixels[x+y*screen->pitch] = STARS_C + (rand() % STARS_NC);
	}
}


// Draw the sea, using the functions from `water.h` to do the color cycling.
static void draw_sea()
{
	int horizon = (float)screen->h * HORIZON;
	SDL_FillRect(screen, &(SDL_Rect){ 0, horizon, screen->w, screen->h }, 254);
	water_replace(water, screen, 254);
}


// Draw a specific building `n` in the position `x`. Returns the position
// for the next building.
static int draw_building(int n, int x)
{
	int *b = buildings[n];
	int i = 0, y;
	int horizon = (float)screen->h * HORIZON;
	Uint8* pixels = ((Uint8*)screen->pixels);

	while(b[i] != 0)
	{
		for(y=horizon; y>horizon-b[i]; y--)
			if(x+i < screen->w)
				pixels[(x+i) + (y * screen->pitch)] = BLACK;
		++i;
	}
	int final_i = i;
	while(i >= 0)
	{
		for(y=horizon; y>horizon-b[i]; y--)
			if((x+(2*final_i)-i-1) < screen->w)
				pixels[(x+(2*final_i)-i-1) + (y * screen->pitch)] = BLACK;
		--i;
	}

	return x+(2*final_i);
}


// Draw the horizon of buildings.
static void draw_buildings()
{
	int horizon = (float)screen->h * HORIZON;
	Uint8* pixels = ((Uint8*)screen->pixels);
	int n_buildings = sizeof(buildings) / sizeof(buildings[0]);
	int x = 0;

	srand(1);

	// draw the buildings
	while(x < screen->w)
		x = draw_building(rand() % n_buildings, x);

	/* draw all buildings:
	int i;
	for(i=0; i<n_buildings; i++)
		x = draw_building(i, x); */

	// draw a line below, so that the city is not a straight line
	for(x=0; x<screen->w; x++)
		if(rand() % 3 == 0)
			pixels[x+((horizon+1)*screen->pitch)] = 0;
}


// Draw everything on the screen.
static void draw_screen()
{
	// draw images
	if(SDL_MUSTLOCK(screen))
		if(SDL_LockSurface(screen) == -1)
			ui_error("Error locking surface.");
	draw_sky();
	draw_sea();
	draw_buildings();
	if(SDL_MUSTLOCK(screen))
		SDL_UnlockSurface(screen);

	// write information on the screen
	SDL_BlitSurface(images[TITLE], NULL, screen, &(SDL_Rect){20,20});
	int x1 = 25, x2 = (x1+1);
	int y1 = 100, y2 = (y1+1);
	Pencil p1 = { screen, alphbeta8, (SDL_Color){255,255,255} };
	Pencil p2 = { screen, alphbeta8, (SDL_Color){0,0,0} };
	y2 = writef(p2, x2, y2, _("Written by %s."), "André Wagner");
	y1 = writef(p1, x1, y1, _("Written by %s."), "André Wagner");
	y2 = writef(p2, x2, y2, _("Version %s"), VERSION);
	y1 = writef(p1, x1, y1, _("Version %s"), VERSION);
	y2 = writef(p2, x2, y2, _("This is free software."));
	y1 = writef(p1, x1, y1, _("This is free software."));

	// draw menu
	menu_x = screen->w - main_menu->sf->w - 40;
	if(screen->w < 450)
		menu_y = screen->h - main_menu->sf->h - 40;
	else
		menu_y = 40;
	SDL_BlitSurface(main_menu->sf, NULL, screen,
			&(SDL_Rect) { menu_x, menu_y });

	SDL_Flip(screen);
}


// Main loop for the title screen.
static void title_loop()
{
	int frame = 0;

	SDL_Event e;
	while(!chosen_region)
	{
		// check for events
		while(SDL_PollEvent(&e))
			switch(e.type)
			{
				case SDL_KEYDOWN:
				case SDL_MOUSEMOTION:
				case SDL_MOUSEBUTTONDOWN:
					if(process_menu(main_menu, &e, menu_x, menu_y))
						draw_screen();
					break;
				case SDL_VIDEORESIZE:
					screen = SDL_SetVideoMode(e.resize.w,
							e.resize.h, 8,
							screen->flags);
					SDL_SetPalette(screen, SDL_LOGPAL|SDL_PHYSPAL, color, 0, 255);
					draw_screen();
					break;
				case SDL_QUIT:
					quit();
					break;
			}

		// wait for next frame
		Uint32 time = SDL_GetTicks() + (1000/60);
		while(SDL_GetTicks() < time)
			SDL_Delay(1);

		// cycle water palette
		water_cycle(water);
		SDL_SetPalette(screen, SDL_LOGPAL|SDL_PHYSPAL, color, 0, 255);

		// cycle star palette
		if(frame % 24 == 0)
		{
			rotate_palette(color, STARS_C, STARS_C+STARS_NC);
			SDL_SetPalette(screen, SDL_LOGPAL|SDL_PHYSPAL,
					&color[STARS_C], STARS_C, STARS_NC);
		}

		++frame;
	}
}


// Called when the user chooses Exit to OS in the menu
static void quit()
{
	atexit(SDL_Quit);
	exit(0);
}


// User chose a region
static void open_region(void* str)
{
	chosen_region = load_region(str);
	assert(chosen_region);
}


// Create main menu
static void create_main_menu()
{
	// create menu
	main_menu = create_menu((OptionMenu*)&menus, pixChicago8, false);
	//main_menu = create_menu((OptionMenu*)&new_region_menu, pixChicago8, false);

	// add existing cities to menu
	RegionType rtype;
	OptionMenu* city_menu;
	for(rtype = 0; rtype < 3; rtype++)
	{
		// load regions
		int i = 0;
		char* regions[1024];
		int n = regions_names(rtype, regions);
		city_menu = malloc(sizeof(OptionMenu) * (n + 3));

		for(i=0; i<n; i++)
			city_menu[i] = (OptionMenu) { regions[i], FUNCTION_CALL,
					&open_region, regions[i] };
		if(rtype == OPEN_ENDED)
			city_menu[i++] = (OptionMenu) { _("Create new region"), FUNCTION_CALL, create_new_region };
		city_menu[i++] = (OptionMenu) { _("Back"), BACK, NULL };
		city_menu[i++] = (OptionMenu) { NULL, 0, NULL };

		i=0;
		while(menus[i].text)
		{
			if(menus[i].data2 == (void*)rtype)
			{
				menus[i].data = city_menu;
				break;
			}
			i++;
		}
	}
}


void free_alloc_memory()
{
	// TODO - free regions menu
	free_menu(main_menu);
}


// Show the game title, with a image and the main menu.
Region* show_title()
{
	chosen_region = NULL;

	create_main_menu();
	setup_title_palette();

	draw_screen();

	title_loop(); // go to the main loop

	free_alloc_memory();

	assert(chosen_region);
	return chosen_region;
}


// Create a new region
static void create_new_region()
{
	char region_name[30] = "";
again:	
	input(QUESTION, region_name, 30, false, 
			_("What is the name of the new region?"));

	if(strcmp(region_name, "") == 0)
	{
		message(WARNING, _("A name must be given to the region"));
		goto again;
	}
	
	// TODO - look for duplicate names
	chosen_region = new_region(region_name);
}
